package com.pesking.core.sqlparser.impl;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import com.pesking.core.SpringBeanUtils;
import com.pesking.core.sqlparser.exception.SQLRuntimeException;
import com.pesking.core.util.ThreadUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.stereotype.Component;

import com.pesking.core.datasource.DatasourceManage;
import com.pesking.core.datasource.Page;
import com.pesking.core.exception.CustomException;
import com.pesking.core.exception.SystemException;
import com.pesking.core.freemarker.FreemarkerManage;
import com.pesking.core.parameter.Parameter;
import com.pesking.core.performance.PerformanceManage;
import com.pesking.core.performance.domain.PerformanceTargetInfoDomain;
import com.pesking.core.sqlparser.SqlParser;
import com.pesking.core.sqlparser.SqlResult;
import com.pesking.core.sqlparser.parameter.SqlParameter;
import com.pesking.core.util.LogUtils;
import com.pesking.core.util.StringUtils;
import com.pesking.databasecompatibility.DBUtils;
import com.pesking.databasecompatibility.DbType;

public class SqlParserImpl implements SqlParser {

	@Override
	public <T> void executeSQL(Connection connection, String statement, Parameter parameter, Page page,	SqlResult<T> result) {
		long startTime = System.currentTimeMillis();
		PreparedStatement ps = null;
		Statement stmt = null;
		ResultSet rs = null;
		if (connection == null){
			throw new SQLRuntimeException("连接对象为空.");
		}
		PerformanceTargetInfoDomain targetInfo = PerformanceManage.getTatgetObject("sqlPerformance");
		try {
			LogUtils.info("参数statement=" + outputLog(statement));
			
			
			if (StringUtils.isBlank(statement)){
				if (StringUtils.isBlank(statement)) {
					LogUtils.warn("SQL语句为空，执行终止，直接返回");
					return;
				}
			}
			
			if (parameter == null) {
				parameter = ThreadUtils.getCurrentParameter();
			}
			
			
			DbType dbType = DatasourceManage.getCurrentDbType();
			
			//设置数据库类型
			parameter.setParameter("DB_TYPE", dbType.getName());	
			//LogUtils.performanceTrace("模板解释前SQL->" + statement);
			String tmpStatement = statement;
			long tmpStart = System.currentTimeMillis();
			statement = FreemarkerManage.parseTemplate(statement,parameter);
			targetInfo.addChildrenPerformanceTarget("模板解释", System.currentTimeMillis() - tmpStart,tmpStatement);
			//LogUtils.performanceTrace("模板解释后SQL->" + statement);
			//LogUtils.debug("模板解释后的statement=" + outputLog(statement));			
			if (StringUtils.isBlank(statement)) {
				LogUtils.debug("SQL语句为空，执行终止，直接返回");
				return;
			}
			String tmp = statement.trim().toUpperCase();
			if (tmp.startsWith("ERROR:")){
				tmp = statement.trim();
				tmp = tmp.substring(6);
				String []arr = tmp.split("@");
				int errorCode;
				String message = "";
				if (arr.length >= 2){
					errorCode = Integer.parseInt(arr[0]);
					message = arr[1];
				}
				else{
					errorCode = -1;
					message   = "系统异常";
				}
				throw new CustomException(errorCode,message);
			}
			else if (tmp.equals("EMPTY_SQL")) {
				LogUtils.debug("SQL语句为EMPTY_SQL，执行终止，直接返回");
				return;
			}
			//SQL兼容处理
			if ("true".equals(SpringBeanUtils.getEnvirnmentProperty("pesking.sql.compatility"))) {
				statement = DBUtils.sqlCompatility(statement, dbType);
			}
			else {
				LogUtils.debug("兼容处理标志pesking.sql.compatility为假，未作SQL兼容处理..........");
			}
			//SQL模板解释
			
			if (page != null) {
				//处理下分页语句
				result.setPage(page);
				statement = paginationStatment(statement,page);
			}
			//SQL参数处理
			final List<SqlParameter> processors = new ArrayList<>();
			final StringBuilder sbSQL = new StringBuilder();
			LogUtils.info("参数解释前SQL->" + statement);
			tmpStart = System.currentTimeMillis();
			parseParameter(statement,parameter,sbSQL,processors);
			statement = sbSQL.toString();
			LogUtils.info("参数解释后SQL->" + statement);
			targetInfo.addChildrenPerformanceTarget("参数解释", System.currentTimeMillis() - tmpStart,formatSQL(statement));
			//LogUtils.debug("SQL参数解释后的statement=" + outputLog(statement));			
			if (dbType.allowUsePreparedStatement()) {
				ps = connection.prepareStatement(statement);
				//处理参数
				setParameter(ps,parameter,processors);
				if (isUpdateSQL(statement)){	
					long s = System.currentTimeMillis();
					int cnt = ps.executeUpdate();
					targetInfo.addChildrenPerformanceTarget("单独更新SQL", System.currentTimeMillis() - s,formatSQL(statement));
					LogUtils.info("本次成功更新记录数[" + cnt + "]-->" + outputLog(statement));
					targetInfo.addExtendProperties("更新SQL次数", 1);
					result.dealUpdateReturnValue(cnt);
				}
				else {
					long s = System.currentTimeMillis();
					rs = ps.executeQuery();
					targetInfo.addChildrenPerformanceTarget("单独查询SQL", System.currentTimeMillis() - s,formatSQL(statement));
					result.dealResultSet(rs);
					targetInfo.addExtendProperties("查询SQL次数", 1);
					//LogUtils.performanceTrace("<<----SQL->" + statement + " 结果集->" + result.getResult());
				}
			}
			else {
				stmt = connection.createStatement();
				if (isUpdateSQL(statement)){	
					long s = System.currentTimeMillis();
					int cnt = stmt.executeUpdate(statement);
					targetInfo.addChildrenPerformanceTarget("单独更新SQL", System.currentTimeMillis() - s,formatSQL(statement));
					LogUtils.info("本次成功更新记录数[" + cnt + "]-->" + outputLog(statement));
					targetInfo.addExtendProperties("更新SQL次数", 1);
					result.dealUpdateReturnValue(cnt);
				}
				else {
					long s = System.currentTimeMillis();
					rs = stmt.executeQuery(statement);
					targetInfo.addChildrenPerformanceTarget("单独查询SQL", System.currentTimeMillis() - s,formatSQL(statement));
					result.dealResultSet(rs);
					targetInfo.addExtendProperties("查询SQL次数", 1);
					//LogUtils.performanceTrace("<<----SQL->" + statement + " 结果集->" + result.getResult());
				}
			}
			
			
		}
		catch(Exception ex) {			
			LogUtils.error("执行SQL[" + outputLog(statement) + "]报错,参数对象[" + parameter + "]，具体原因->" + ExceptionUtils.getStackTrace(ex));
			LogUtils.writeKeyLog("本次执行SQL[" + statement + "]\n参数[" + parameter + "]，具体原因->" + ex.getMessage(), 0);
			targetInfo.addFailedRequests();
			throw SystemException.throwSystemException(ex);			
		}
		finally {
			if (rs != null) {
				try {
					rs.close();
				}
				catch(SQLException e) {}
			}
			if (ps != null) {
				try {
					ps.close();
				}
				catch(SQLException e) {}
			}
			long endTime = System.currentTimeMillis();
			long interval = endTime - startTime;
			
			if (interval > 10000) {
				LogUtils.writeKeyLog("本次执行SQL[" + statement + "]\n参数[" + parameter + "]\n耗时[" + interval + "]..........", interval);
			}
			else {
				LogUtils.debug("本次执行SQL[" + statement + "]\n参数[" + parameter + "]\n耗时[" + interval + "]..........");
			}
			targetInfo.addCompleteRequests(interval,statement);
		}
	}
	
	private String formatSQL(String sql) {
		sql = org.springframework.util.StringUtils.trimAllWhitespace(sql);
		return sql;
	}
	
	private boolean isUpdateSQL(String sql) {
		String tmpSQL = sql.trim().toLowerCase();
		if (tmpSQL.startsWith("update") 
				|| tmpSQL.startsWith("insert") 
				|| tmpSQL.startsWith("delete")
				|| tmpSQL.startsWith("create")
				|| tmpSQL.startsWith("truncate") //3.5.2版本新增的
				|| tmpSQL.startsWith("drop")
				|| tmpSQL.startsWith("replace")){	
			return true;
		}
		else {
			return false;
		}
	}
	
	/**
	 * 处理参数
	 * @param ps
	 * @param parameter
	 * @param processors
	 */
	private void setParameter(PreparedStatement ps, Parameter parameter, List<SqlParameter> processors) throws SQLException{
		if (processors != null) {
			int index = 1;
			for (SqlParameter processor : processors) {
				processor.getProcessor().setParameter(ps, parameter, index++,processor);
			}
		}
		
	}

	/**
	 * 处理分页SQL语句
	 * @param statement
	 * @param page
	 * @return
	 */
	private String paginationStatment(String statement, Page page) {
		DbType dbType = DatasourceManage.getCurrentDbType();
		int rowStart = page.getStartPosition();
		int rowEnd   = page.getEndPosition();
		return dbType.handlePagationStatement(statement, rowStart, rowEnd);
	}
	
	/**
	 * 解释参数
	 * @param sql
	 * @param parameter
	 * @param sbSQL
	 * @param processors
	 */
	private void parseParameter(String sql,Parameter parameter,StringBuilder sbSQL,final List<SqlParameter> processors) {
		String sqlTmp = null;
		int index;
		
		if (sql != null && sql.trim().length() > 0)
		{
			index = sql.indexOf("[");
			if (index == -1)
			{
				sbSQL.append(sql);
				return;
			}
			else
			{
				sqlTmp = sql.substring(0, index);
				sbSQL.append(sqlTmp);
				
				sql = sql.substring(index + 1);
				index = sql.indexOf("]");
				if (index == -1)
				{
					throw new SQLRuntimeException("SQL语句书写格式有误，[不匹配.....");
				}
				sqlTmp = sql.substring(0,index);
				SqlParameter sqlParameter = new SqlParameter(sqlTmp);
				sqlParameter.getProcessor().handle(sbSQL, parameter,processors,sqlParameter);
				sql = sql.substring(index + 1);
				if (sql != null && sql.length() > 0){
					parseParameter(sql,parameter,sbSQL,processors);
				}
			}
		}	
	}
	
	private String outputLog(String statement){
		if (statement != null) {
			statement = StringUtils.replace(statement, "\r", "");
			statement = StringUtils.replace(statement, "\n", " ");
		}
		return statement;
	}

	@Override
	public int getOrder() {
		return 0;
	}
}
